﻿using System;

namespace _099_Lambda表达式_捕获外部变量
{
    class Program
    {
        static void Main(string[] args)
        {
            //4.3.2 捕获外部变量
            // Lambda表达式可以引用方法内的内部变量和参数（外部变量）。
            /*
            int factor = 2;
            Func<int, int> multiplier = n => n * factor;
            Console.WriteLine(multiplier(3)); // 6
            */

            // Lambda表达式引用的外部变量称为捕获变量。捕获变量的表达式称为一个闭包。
            // 捕获的变量在真正调用委托时被赋值，而不是在捕获时赋值：
            /*
            int factor = 2;
            Func<int, int> multiplier = n => n * factor;
            factor = 10;
            Console.WriteLine(multiplier(3)); // 30
            */

            //Lambda表达式可以自动更新捕获变量：
            /*
            int seed = 0;
            Func<int> natural = () => seed++;
            Console.WriteLine(natural()); // 0
            Console.WriteLine(natural()); // 1
            Console.WriteLine(seed); // 2
            */

            // 捕获变量的声明周期可以延伸到和委托的声明周期相同。
            //下面的例子中，局部变量seed本应在Natural执行完后消失，但因为seed被捕获了，它的生命周期延长到和捕获它的委托natural的生命周期相同。
            Func<int> natural = Natural();
            Console.WriteLine(natural()); // 0
            Console.WriteLine(natural()); // 1

            // 在lambda表达式内实例化的局部变量，在每次调用委托实例期间是唯一的。
            //如果我们把上例改成在lambda表达式内实例化seed，程序的结果（不是本例预期的结果）将与前面不同：
            Func<int> natural_1 = Natural_1();
            Console.WriteLine(natural_1()); // 0
            Console.WriteLine(natural_1()); // 0
            // 提示：在内部捕获是通过把被捕获的变量“提升”到私有类的字段实现。当方法被调用时，实例化该类，并将其生命周期绑定在委托的实例上。
            
            Console.ReadKey();
        }
        static Func<int> Natural()
        {
            int seed = 0;
            return () => seed++; // 返回一个闭包
        }
        static Func<int> Natural_1()
        {
            return () => { int seed = 0; return seed++; };
        }
    }
}